// This is the main DLL file.

#include "stdafx.h"

#include "libmusico.h"

using namespace libmusico;
using namespace System;
using namespace System::IO;
using namespace System::Xml;
using namespace System::Collections::Generic;

//// Message ////

// Constructor
Message::Message()
{
	channel = 1;
	status = 0x90;
	note = 0x45;
	velocity = 0x3c;
}

Message::Message(int c, int s, int n, int v)
{
	channel = c;
	status = (unsigned char)s;
	note = (unsigned char)n;
	velocity = (unsigned char)v;
}

unsigned int Message::getChannel() {
	return channel;
}

unsigned char Message::getStatus() {
	return status;
}

unsigned char Message::getMidiOne() {
	return note;
}

unsigned char Message::getMidiTwo() {
	return velocity;
}

// Returns the MIDI message
unsigned long int Message::getMIDI()
{
	unsigned long int out;
	out = (int)status + channel - 1 ;
	out += 16*16 * (int)note;
	out += 16*16*16*16 * (int)velocity;
	return out;
}

// Turns the message to OFF
void Message::turnOFF()
{
	status = 0x80;
}

//// Command ////

Command::Command()
{
	messages = gcnew Dictionary<String^, Message^>(); 
}

void Command::addMessage(System::String ^ key, libmusico::Message ^ msg)
{
	messages[key] = msg;
}

void Command::delMessage(System::String ^ key)
{
	messages->Remove(key);
}

void Command::getMessage(System::String ^ key, libmusico::Message ^% msg)
{
	msg = messages[key];
}

String^ Command::print()
{
	String^ out = gcnew String("");
	for each(KeyValuePair<String^, Message^> kvp in messages)
	{
		out->Insert(out->Length, kvp.Key->ToString());
		out->Insert(out->Length, " : ");
		out->Insert(out->Length, kvp.Value->getMIDI().ToString());
		out->Insert(out->Length, " | ");
	}
	return gcnew String("");
}

void Command::send(Int16 midi, Int32 time)
{
	HMIDIOUT midiout;
	MMRESULT mmr = midiOutOpen(&midiout, midi, NULL, 0, CALLBACK_NULL);
	for each (KeyValuePair<String^, Message^> kvp in messages)
	{
		midiOutShortMsg(midiout, kvp.Value->getMIDI());
	}
	Sleep(time);
	mmr = midiOutClose(midiout);
}

void Command::send(Int16 midi)
{
	this->send(midi,1000);
}

void Command::stop(Int16 midi, System::String ^ key)
{

}

void Command::writeTo(System::String ^ filename, System::String^ id)
{
	StreamWriter^ sw = gcnew StreamWriter(filename);
	// XML file header
	sw->WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
	sw->WriteLine("<COMMAND>");
	sw->WriteLine("<{1}> {0} </{1}>", id, "ID");
	// Writing each message
	for each (KeyValuePair<String^, Message^> kvp in messages)
	{
		sw->WriteLine("<MESSAGE>");
		sw->WriteLine("  <{1}> {0} </{1}>", kvp.Key, "ID");
		sw->WriteLine("  <{1}> {0} </{1}>", Convert::ToInt16(kvp.Value->channel), "CHANNEL");
		sw->WriteLine("  <{1}> {0} </{1}>", Convert::ToInt16(kvp.Value->status), "STATUS");
		sw->WriteLine("  <{1}> {0} </{1}>", Convert::ToInt16(kvp.Value->note), "MIDI1");
		sw->WriteLine("  <{1}> {0} </{1}>", Convert::ToInt16(kvp.Value->velocity), "MIDI2");
		sw->WriteLine("</MESSAGE>");
	}
	sw->WriteLine("</COMMAND>");
	sw->Close();
}

String^ Command::loadFrom(String^ path)
{
	messages->Clear();
	String^ id = gcnew String("");
	int c, s, n, v;					// buffer for message parameters
	String^ out = gcnew String("");
	XmlReader^ xr = XmlReader::Create(path);
	// Parsing the command ID
	xr->Read();
	xr->ReadStartElement("COMMAND");
		xr->ReadStartElement("ID");
			out = xr->ReadString();
		xr->ReadEndElement();
	// Parsing the messages
	bool NewMessage = true;
	while (xr->Read()){
		if (xr->IsStartElement()){
			if (xr->Name == "ID"){id = gcnew String(xr->ReadString());}
			if (xr->Name == "CHANNEL"){c = Convert::ToInt16(xr->ReadString());}
			if (xr->Name == "STATUS"){s = Convert::ToInt16(xr->ReadString());}
			if (xr->Name == "MIDI1"){n = Convert::ToInt16(xr->ReadString());}
			if (xr->Name == "MIDI2"){v = Convert::ToInt16(xr->ReadString());
				this->addMessage(id, gcnew Message(c, s, n, v));
			}
		}
	}
	String^ str = gcnew String(out);
	return out;
}